var Layer2DDefaultMaxObject = 10;
function gewLayer2D(game, scene2D)
{
  this.game = game;
  this.gl = game.gl;
  this.scene2D = scene2D;
  this.objectList = [];
  this.vertexBuff = new WebGLFloatArray(8 * Layer2DDefaultMaxObject); //2 float per vertex, 4 vertex per object
  this.translateBuff = new WebGLFloatArray(8 * Layer2DDefaultMaxObject);
  this.texcoordBuff = new WebGLFloatArray(8 * Layer2DDefaultMaxObject); //2 float per texcoord, 4 texcoord per object
  this.indicesBuff = new WebGLUnsignedShortArray(6*Layer2DDefaultMaxObject); // 6 index per object
  this.maxObject = Layer2DDefaultMaxObject;
  this.vBuffOffset = [];// The stack contain these free offset in render buffer
  this.glBuffers = {};
  var gl = this.gl;
  this.glBuffers.vertex = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.vertex);
  gl.bufferData(gl.ARRAY_BUFFER, this.vertexBuff, gl.DYNAMIC_DRAW);
  this.glBuffers.texCoord = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.texCoord);
  gl.bufferData(gl.ARRAY_BUFFER, this.texcoordBuff, gl.DYNAMIC_DRAW);
  this.glBuffers.translate = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.translate);
  gl.bufferData(gl.ARRAY_BUFFER, this.translateBuff, gl.DYNAMIC_DRAW);
  this.glBuffers.indices = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.glBuffers.indices);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indicesBuff, gl.DYNAMIC_DRAW);
  this.isNeedUpdateIndices = false;
}

gewLayer2D.prototype.render = function()
{
  //console.log("Render a layer!");
  var gl = this.gl;
  var isVBufChange = false;
  var isTBufChange = false;
  var isTransBufChange = false;
  var objList = this.objectList;
  var objNum = objList.length;
  var subTexList = this.scene2D.atlas.subTexList;
  var vbuff = this.vertexBuff;
  var tbuff = this.texcoordBuff;
  var transbuff = this.translateBuff;
  var ibuff = this.indicesBuff
  for (var i = 0; i < objNum; i++) 
  {
    var obj = objList[i];
    if( obj.isUpdateDepth)
    {
      this.isNeedUpdateIndices = true;
      obj.isUpdateDepth = false;
    }
    if (obj.isUpdatePosition) 
    {
      var offset = obj.vBufOffset;
      var game = obj.game;
      if(obj.isVisible)
      {
        //Vertex 0
        vbuff[offset] = obj.v0.x;
        vbuff[offset + 1] = obj.v0.y;
        //Vertex 1
        vbuff[offset + 2] = obj.v1.x;
        vbuff[offset + 3] = obj.v1.y;
        //Vertex 2
        vbuff[offset + 4] = obj.v2.x;
        vbuff[offset + 5] = obj.v2.y;
        //Vertex 3
        vbuff[offset + 6] = obj.v3.x;
        vbuff[offset + 7] = obj.v3.y;
      }
      else
      {
        vbuff[offset] = 0;
        vbuff[offset + 1] = 0;
        vbuff[offset + 2] = 0;
        vbuff[offset + 3] = 0;
        vbuff[offset + 4] = 0;
        vbuff[offset + 5] = 0;
        vbuff[offset + 6] = 0;
        vbuff[offset + 7] = 0;
      }
      isVBufChange = true;
      obj.isUpdatePosition = false;
    }
    if (obj.isUpdateTexcoord) 
    {
      if(obj.isUpdateSubTexName)
      {
        obj.subTexture = subTexList[obj.subTextureName];  
        obj.isUpdateSubTexName =  false;
      }
      var subTex = obj.subTexture;     
      var wo = subTex.woffset;
      var ho = subTex.hoffset;
      var w = subTex.width;
      var h = subTex.height;
      var atlasWidth = subTex.mainTexture.width;
      var atlasHeight = subTex.mainTexture.height;          
      var tleft =  wo+ obj.subRegionLeft/atlasWidth;;
      var ttop = ho+ obj.subRegionTop/atlasHeight;
      if(obj.subRegionWidth==0&&
         obj.subRegionHeight==0)
      {
        var tright = wo+w;
        var tbottom = ho+h;
      }
      else
      {
        var tright = wo+ (obj.subRegionLeft+ obj.subRegionWidth)/atlasWidth;
        var tbottom = ho+ (obj.subRegionTop+obj.subRegionHeight)/atlasHeight;
      }
      offset = obj.vBufOffset;
      //Texcoord 0
      tbuff[offset] = tleft;
      tbuff[offset + 1] = ttop;
      //Texcoord 1
      tbuff[offset + 2] = tright;
      tbuff[offset + 3] = ttop;
      //Texcoord 2
      tbuff[offset + 4] = tleft;
      tbuff[offset + 5] = tbottom;
      //Texcoord 3
      tbuff[offset + 6] = tright;
      tbuff[offset + 7] = tbottom;
      isTBufChange = true;
      obj.isUpdateTexcoord = false;
    }
    if(obj.isUpdateTranslate)
    {
      offset = obj.vBufOffset;
      var x = obj.translate.x;
      var y = obj.translate.y;
      transbuff[offset]   = x;
      transbuff[offset+1] = y;    
      transbuff[offset+2] = x;
      transbuff[offset+3] = y;
      transbuff[offset+4] = x;
      transbuff[offset+5] = y;      
      transbuff[offset+6]  = x;
      transbuff[offset+7] = y;
      isTransBufChange = true;
      obj.isUpdateTranslate = false;
    }
  }
  if( this.isNeedUpdateIndices)
  {
    // Sort sprite list
    objList.sort(gewSprite2DCompareDepth);
    for (i = 0; i < objNum; i++) 
    {
      var obj = objList[i];
      var index_offset = obj.vBufOffset/2;
      var start = i*6;
      ibuff[start] = index_offset;
      ibuff[start+1] = index_offset+1;
      ibuff[start+2] = index_offset+2;
      ibuff[start+3] = index_offset+1;
      ibuff[start+4] = index_offset+2;
      ibuff[start+5] = index_offset+3;
    }
  }
  gl.uniform4f(Scene2DShader.color, 1, 1, 1, 1);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.vertex);
  if (isVBufChange) 
    gl.bufferSubData(gl.ARRAY_BUFFER,0, this.vertexBuff);
  gl.vertexAttribPointer(Scene2DShader.position, 2, gl.FLOAT, false, 0, 0);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.texCoord);
  if (isTBufChange) 
    gl.bufferSubData(gl.ARRAY_BUFFER,0, this.texcoordBuff);
  gl.vertexAttribPointer(Scene2DShader.texCoord, 2, gl.FLOAT, false, 0, 0);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.translate);
  if (isTransBufChange) 
    gl.bufferSubData(gl.ARRAY_BUFFER,0, this.translateBuff);
  gl.vertexAttribPointer(Scene2DShader.translate, 2, gl.FLOAT, false, 0, 0);
  
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.glBuffers.indices);
  if( this.isNeedUpdateIndices)
    gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER,0, this.indicesBuff);
  this.isNeedUpdateIndices = false;  
  if (objNum > 0) 
    gl.drawElements(gl.TRIANGLES,objNum*6,gl.UNSIGNED_SHORT,0);
}
gewLayer2D.prototype.getVBufOffset = function()
{
  var offset = this.vBuffOffset.pop();
  if (!offset) 
  {
    offset = 8 * (this.objectList.length - 1);
    return offset;
  } else 
    return offset;
}
gewLayer2D.prototype.insert = function(obj)
{
  // If the number of Object > maxOBject, must allocate more memory for render buff
  if (this.objectList.length > this.maxObject) 
  {
    delete this.vertexBuff;
    delete this.texcoordBuff;
    delete this.translateBuff;
    delete this.indicesBuff;    
    this.vertexBuff = new WebGLFloatArray(8 * this.objectList.length * 2); 
    this.translateBuff = new WebGLFloatArray(8 * this.objectList.length * 2);
    this.texcoordBuff = new WebGLFloatArray(8 * this.objectList.length * 2); 
    this.indicesBuff = new WebGLUnsignedShortArray(6*this.objectList.length * 2); 
    this.maxObject = this.objectList.length * 2;
    
    var gl = this.gl;
    gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.vertex);
    gl.bufferData(gl.ARRAY_BUFFER, this.vertexBuff, gl.DYNAMIC_DRAW);
    gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.texCoord);
    gl.bufferData(gl.ARRAY_BUFFER, this.texcoordBuff, gl.DYNAMIC_DRAW);
    gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.translate);
    gl.bufferData(gl.ARRAY_BUFFER, this.translateBuff, gl.DYNAMIC_DRAW);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.glBuffers.indices);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indicesBuff, gl.DYNAMIC_DRAW);
  }
  
  var listLength = this.objectList.push(obj);
  var vBufOffset = this.getVBufOffset();
  obj.vBufOffset = vBufOffset
  /*
  var ioffset = (vBufOffset/8)*6;
  var ibuf = this.indicesBuff;
  var index = vBufOffset/2;
  ibuf[ioffset] = index;
  ibuf[ioffset + 1] = index+1;
  ibuf[ioffset + 2] = index+2;
  ibuf[ioffset + 3] = index+1;
  ibuf[ioffset + 4] = index+2;
  ibuf[ioffset + 5] = index+3;
  */
  this.isNeedUpdateIndices = true;
  obj.listIndex = listLength - 1;
}
gewLayer2D.prototype.remove = function(obj)
{
  var ibuf = this.indicesBuff;
  var ioffset = obj.iBufOffset;
  ibuf[ioffset] = 0;
  ibuf[ioffset + 1] = 0;
  ibuf[ioffset + 2] = 0;
  ibuf[ioffset + 3] = 0;
  ibuf[ioffset + 4] = 0;
  ibuf[ioffset + 5] = 0;
  this.isNeedUpdateIndices = true;
  this.vBuffOffset.push(obj.vBufOffset);
  this.objectList.splice(obj.listIndex, 1);
}
var Scene2DVertexShaderStr = "\
attribute vec2 position;\
attribute vec2 translate;\
attribute vec2 texCoord0;\
uniform vec2 canvasSize;\
varying vec2 oTexCoord0;\
void main(void)\
{\
  gl_Position = vec4( (position.x+translate.x)/canvasSize.x -1.0,(-position.y-translate.y)/canvasSize.y+1.0,0.,1.0);\
  oTexCoord0 = texCoord0;\
}";
var Scene2DFragmentShaderStr = "\
varying vec2 oTexCoord0;\
uniform vec4 color;\
uniform sampler2D texture0;\
void main(void)\
{\
  gl_FragColor = texture2D(texture0, oTexCoord0.st)*color;\
}";
var Scene2DShader = {};
function gewScene2D(game)
{
  this.atlas = null;
  this.game = game;
  this.gl = game.gl;
  this.layer0 = new gewLayer2D(game, this);
  this.canvasHalfSize = new WebGLFloatArray([game.width / 2, game.height / 2]);
  if (!Scene2DShader.vs) 
  {
    var gl = this.gl;
    Scene2DShader.vs = createVertexShader(gl, Scene2DVertexShaderStr);
    Scene2DShader.fs = createFragmentShader(gl, Scene2DFragmentShaderStr);
    Scene2DShader.sp = gl.createProgram();
    var program = Scene2DShader.sp;
    gl.attachShader(program, Scene2DShader.vs);
    gl.attachShader(program, Scene2DShader.fs);
    gl.linkProgram(program);
    //if (!gl.getProgrami(program, gl.LINK_STATUS))
    //{
    //  alert(gl.getProgramInfoLog(Scene2DShader));
    //}
    Scene2DShader.position = gl.getAttribLocation(program, "position");
    Scene2DShader.translate = gl.getAttribLocation(program, "translate");
    Scene2DShader.texCoord = gl.getAttribLocation(program, "texCoord0");
    Scene2DShader.texture0 = gl.getUniformLocation(program, "texture0");
    Scene2DShader.canvasSize = gl.getUniformLocation(program, "canvasSize");
    Scene2DShader.color = gl.getUniformLocation(program, "color");
  }
  this.rootView = new gewUIView(null);
  this.rootView.scene2d = this;
  this.keyEventReceiver = null; // the gewUIView that receiver keyboard event
}
gewScene2D.prototype.setKeyEventReceiver = function(uiview)
{
  this.keyEventReceiver = uiview;
}
gewScene2D.prototype.onMouseDown = function(e)
{
  this.rootView.dispatchMouseDown(e);
}
gewScene2D.prototype.onMouseUp = function(e)
{
  this.rootView.dispatchMouseUp(e);
}
gewScene2D.prototype.onMouseMove = function(e)
{
  this.rootView.dispatchMouseMove(e);
}
gewScene2D.prototype.onKeyDown = function(e)
{
  var keyReceiver  = this.keyEventReceiver;
  if(keyReceiver)
  {
    if(keyReceiver.defaultKeyDownBehavior)
      keyReceiver.defaultKeyDownBehavior(e);  
    keyReceiver.onKeyDown(e);
  }
}
gewScene2D.prototype.onKeyUp = function(e)
{
  var keyReceiver  = this.keyEventReceiver;
  if(keyReceiver)
  {
    if(keyReceiver.defaultKeyUpBehavior)
      keyReceiver.defaultKeyUpBehavior(e);  
    keyReceiver.onKeyUp(e);
  }  
}

gewScene2D.prototype.render = function()
{
  var gl = this.gl;
  //gl.useProgram(Scene2DShader.sp);
  var atlas = this.atlas;
  if (atlas.isReady&&atlas.textureList[0].isReady) 
  {
    gl.enable(gl.BLEND);
    //gl.enable(gl.DEPTH_TEST);
    //gl.disable(gl.DEPTH_TEST);
    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    gl.useProgram(Scene2DShader.sp);
    gl.uniform2fv(Scene2DShader.canvasSize, this.canvasHalfSize);
    gl.uniform1i(Scene2DShader.texture0, 0);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, atlas.textureList[0].glName);
    gl.enableVertexAttribArray(Scene2DShader.position);
    gl.enableVertexAttribArray(Scene2DShader.translate);
    gl.enableVertexAttribArray(Scene2DShader.texCoord);
    this.layer0.render();
    gl.disableVertexAttribArray(Scene2DShader.position);
    gl.disableVertexAttribArray(Scene2DShader.translate);
    gl.disableVertexAttribArray(Scene2DShader.texCoord);
    gl.disable(gl.BLEND);
  }
}
